package edu.map.aamas.gui.psp;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.util.LinkedHashMap;

import javax.swing.JPanel;

import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor;
import com.l2fprod.common.propertysheet.DefaultProperty;
import com.l2fprod.common.propertysheet.Property;
import com.l2fprod.common.propertysheet.PropertyEditorRegistry;
import com.l2fprod.common.propertysheet.PropertySheetDialog;
import com.l2fprod.common.propertysheet.PropertySheetPanel;

import edu.map.aamas.rnn.RNN;

public class PropertiesPanel extends JPanel {

	private static String NUMBER_NEURONS = "Number of Neurons";
	private static String CONTROLLER = "Controller";
	private static String USE_STATIC_SEED = "Use static random seed";
	private static String SEED = "Random seed";
	private static String DELAY = "Time between interactions (millis)";
	private static String HOMEOSTASIS = "Homeostasis";
	private static String PROPRIOCEPTION = "Proprioception";
	private static String NEURAL_PLASTICITY = "Neural Plasticity";
	private static String LOGGING = "Enable Logging";
	
	
	private RNN rnr;
	private LinkedHashMap<String, DefaultProperty> properties = new LinkedHashMap<String, DefaultProperty>();
	private PropertyEditorRegistry per = null;
	
	private PropertyEditorRegistry getEditorRegistry() {
		if (per == null) {
			per = new PropertyEditorRegistry();			
			per.registerDefaults();			
		}
		
		return per;
	}
	
	
	/**
	 * Convenience method to create a default property object from various parameters
	 * @param propertyName The name of the property
	 * @param categoryName The category of the property to be created
	 * @param propertyClass The class of the values of this property
	 * @param value The current value for this property
	 * @param isEditable Sets whether the property can be edited by the user
	 * @return A DefaultProperty object @see DefaultProperty
	 */
	public static DefaultProperty getPropertyInstance(String propertyName, String categoryName, Class propertyClass, Object value, boolean isEditable) {

		DefaultProperty dp = getPropertyInstance(propertyName, propertyClass, value, isEditable);
		dp.setCategory(categoryName);
		return dp;
	}

	/**
	 * Convenience method to create a default property object from various parameters
	 * @param propertyName The name of the property
	 * @param propertyClass The class of the values of this property
	 * @param value The current value for this property
	 * @param isEditable Sets whether the property can be edited by the user
	 * @return A DefaultProperty object @see DefaultProperty
	 */
	public static DefaultProperty getPropertyInstance(String propertyName, Class propertyClass, Object value, boolean isEditable) {
		DefaultProperty property = new DefaultProperty();
		property.setName(propertyName);
		property.setDisplayName(propertyName);
		property.setType(propertyClass);
		property.setValue(value);
		property.setEditable(isEditable);

		return property;
	}

	/**
	 * Convenience method to create a default property object from various parameters
	 * @param propertyName The name of the property
	 * @param categoryName The category of the property to be created
	 * @param propertyClass The class of the values of this property
	 * @param value The current value for this property
	 * @param isEditable Sets whether the property can be edited by the user
	 * @param shortDescription Sets a short description text.
	 * @return A DefaultProperty object @see DefaultProperty
	 */
	public static DefaultProperty getPropertyInstance(String propertyName,
			String categoryName, Class propertyClass, Object value,
			boolean isEditable, String shortDescription)
	{
		DefaultProperty property = getPropertyInstance(propertyName,
				categoryName, propertyClass, value, isEditable);
		property.setShortDescription(shortDescription);
		return property;
	}
	
	public PropertySheetPanel getPsp() {
		PropertySheetPanel psp = new PropertySheetPanel();
		
		DefaultProperty dp;
		PropertyEditorRegistry registry = getEditorRegistry();
		
		dp = getPropertyInstance(NUMBER_NEURONS, "Neural Network", Integer.class, rnr.size, true);
		properties.put(NUMBER_NEURONS, dp);
		
		dp = getPropertyInstance(HOMEOSTASIS, "Neural Network", Boolean.class, rnr.homeostasis, true);
		properties.put(HOMEOSTASIS, dp);

		dp = getPropertyInstance(PROPRIOCEPTION, "Neural Network", Boolean.class, rnr.proprioception, true);
		properties.put(PROPRIOCEPTION, dp);
		
		dp = getPropertyInstance(NEURAL_PLASTICITY, "Neural Network", Boolean.class, rnr.plasticity, true);
		properties.put(NEURAL_PLASTICITY, dp);
		
		dp = getPropertyInstance(CONTROLLER, "Neural Network", String.class, rnr.currentController, true);		
		properties.put(CONTROLLER, dp);
		ComboBoxPropertyEditor editor = new ComboBoxPropertyEditor();
		editor.setAvailableValues(rnr.possibleControllers);
		editor.setValue(rnr.currentController);
		registry.registerEditor(dp, editor);
				
		dp = getPropertyInstance(DELAY, "Neural Network", Integer.class, rnr.millisBetweenIterations, true);		
		properties.put(DELAY, dp);
		
		dp = getPropertyInstance(USE_STATIC_SEED, "Neural Network", Boolean.class, !rnr.useRandomSeed, true);		
		properties.put(USE_STATIC_SEED, dp);
		
		dp = getPropertyInstance(SEED, "Neural Network", Long.class, rnr.randomSeed, true);		
		properties.put(SEED, dp);
		
		dp = getPropertyInstance(LOGGING, "Logging", Boolean.class, rnr.loggingEnabled, true);		
		properties.put(LOGGING, dp);
		
		
		psp.setProperties(properties.values().toArray(new Property[]{}));
		psp.setEditorFactory(registry);
		return psp;
		
	}
	
	public PropertiesPanel(RNN rnr) {
		this.rnr = rnr;
	}
	
	public static void editRNRProperties(RNN rnr) {
		PropertiesPanel pp = new PropertiesPanel(rnr);
		PropertySheetPanel psp = pp.getPsp();
		PropertySheetDialog dialog = new PropertySheetDialog();
		dialog.setSize(400, 500);
		Dimension size = dialog.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (scr.width - size.width) / 2;
		int y = (scr.height - size.height) / 2;
		dialog.setBounds(x, y, size.width, size.height);
		dialog.setTitle("RNN Properties");
		dialog.getBanner().setTitle("RNN Properties");
		dialog.getBanner().setSubtitle("Recurrent Neural Network parameters");
		dialog.getContentPane().add(psp);
		dialog.setModal(true);
		boolean ask = dialog.ask();
		if (ask) 
			setProperties(psp.getProperties(), rnr);
	}
	
	public static void setProperties(Property[] props, RNN rnr) {
		boolean needsRestart = false;
		for (Property prop : props) {
			if (prop.getName().equals(NUMBER_NEURONS)) {
				int lastSize = rnr.size;
				rnr.size = (Integer) prop.getValue();				
				needsRestart = lastSize != rnr.size;			 
			}
			else if (prop.getName().equals(SEED))
				rnr.randomSeed = (Long) prop.getValue();
			else if (prop.getName().equals(USE_STATIC_SEED))
				rnr.useRandomSeed = !(Boolean) prop.getValue();
			else if (prop.getName().equals(DELAY))
				rnr.millisBetweenIterations = (Integer) prop.getValue();
			else if (prop.getName().equals(CONTROLLER))
				rnr.currentController = (String) prop.getValue();
			else if (prop.getName().equals(HOMEOSTASIS))
				rnr.homeostasis = (Boolean) prop.getValue();
			else if (prop.getName().equals(PROPRIOCEPTION))
				rnr.proprioception = (Boolean) prop.getValue();
			else if (prop.getName().equals(NEURAL_PLASTICITY)) {
				rnr.plasticity = (Boolean) prop.getValue();
			}
			else if (prop.getName().equals(LOGGING)) {
				rnr.loggingEnabled = (Boolean) prop.getValue();
			}
			if (needsRestart)
				//FIXME ask user first
				rnr.init();
		}
	}
	
	public static void main(String[] args) {
		RNN rnr = RNN.getInstance();
		PropertiesPanel.editRNRProperties(rnr);
	}
}
